package com.example.hospital.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.example.common.util.annotation.CustomSuppressWarnings;
import com.example.common.util.constant.HospitalConstants;
import com.example.common.util.enums.BusinessEnum;
import com.example.common.util.exception.BusinessException;
import com.example.common.util.exception.DateException;
import com.example.common.util.utils.DateUtils;
import com.example.common.util.utils.StringUtils;
import com.example.hospital.respository.ScheduleRepository;
import com.example.hospital.service.DepartmentService;
import com.example.hospital.service.HospitalService;
import com.example.hospital.service.ScheduleService;
import com.example.model.domain.entity.hospital.*;
import com.example.model.vo.hospital.BookingScheduleRuleVo;
import com.example.model.vo.hospital.ScheduleOrderVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author juzipi
 * @Date 2021/5/6 14:24
 * @Info
 */
@Service
public class ScheduleServiceImpl implements ScheduleService {

    @Autowired
    private ScheduleRepository scheduleRepository;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private HospitalService hospitalService;

    @Autowired
    private DepartmentService departmentService;


    @Override
    public Boolean insertSchedule(Map<String, Object> parameterMap) {
        String string = JSONObject.toJSONString(parameterMap);
        Schedule schedule = JSONObject.parseObject(string, Schedule.class);
        Schedule scheduleExists = scheduleRepository.getScheduleByHoscodeAndHosScheduleId(schedule.getHoscode(), schedule.getHosScheduleId());
        if (StringUtils.isNotNull(scheduleExists)){
            //非空就是更新
            scheduleExists.setUpdateTime(DateUtils.getNowDate());
            Schedule update = scheduleRepository.save(scheduleExists);
            return true;
        }
        schedule.setCreateTime(DateUtils.getNowDate());
        schedule.setUpdateTime(schedule.getCreateTime());
        schedule.setDeleted(HospitalConstants.DELETED_VALUE_FALSE);
        Schedule save = scheduleRepository.save(schedule);
        return true;
    }


    @Override
    public Page<Schedule> getSchedulePage(Integer pageNum, Integer pageSize, String hoscode) {
        //方法都是其他几个接口都是类似的，改改参数就行了
        Pageable pageable = PageRequest.of(pageNum-1, pageSize);
        Schedule schedule = new Schedule();
        schedule.setHoscode(hoscode);
        schedule.setDeleted(HospitalConstants.DELETED_VALUE_FALSE);//查询未删除的
        //CONTAINING：包含，可能是类似模糊查询的吧，后面的是不区分大小写？
        ExampleMatcher exampleMatcher = ExampleMatcher.matching().withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING).withIgnoreCase(true);
        return scheduleRepository.findAll(Example.of(schedule, exampleMatcher), pageable);
    }


    @Override
    public Boolean deleteSchedule(String hoscode, String hpScheduleId) {
        Schedule schedule = scheduleRepository.getScheduleByHoscodeAndHosScheduleId(hoscode, hpScheduleId);
        schedule.setDeleted(HospitalConstants.DELETED_VALUE_TRUE);
        schedule.setUpdateTime(new Date());
        Schedule save = scheduleRepository.save(schedule);
        return true;
    }


    @Override
    public Map<String, Object> getScheduleRule(Long pageNum, Long pageSize, String hoscode, String depcode) {
        //构造查询条件
        Criteria criteria = Criteria.where(HospitalConstants.HOSCODE).is(hoscode).and(HospitalConstants.DEPCODE).is(depcode);
        //根据工作日workDate期进行分组
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria), Aggregation.group(HospitalConstants.WORK_DATE)
                        .first(HospitalConstants.WORK_DATE).as(HospitalConstants.WORK_DATE)
                        .count().as(HospitalConstants.DOC_COUNT)//统计号源量
                        .sum(HospitalConstants.RESERVED_NUMBER).as(HospitalConstants.RESERVED_NUMBER)
                        .sum(HospitalConstants.AVAILABLE_NUMBER).as(HospitalConstants.AVAILABLE_NUMBER),
                //排序
                Aggregation.sort(Sort.Direction.DESC, HospitalConstants.WORK_DATE),
                Aggregation.skip((pageNum - 1) * pageSize),
                Aggregation.limit(pageSize));
        System.out.println("aggregation = " + aggregation);
        //执行查询
        AggregationResults<BookingScheduleRuleVo> aggregationResults = mongoTemplate.aggregate(aggregation, Schedule.class, BookingScheduleRuleVo.class);
        List<BookingScheduleRuleVo> bookingScheduleRuleVos = aggregationResults.getMappedResults();
        //分组查询总记录数
        Aggregation totalAgg = Aggregation.newAggregation(Aggregation.match(criteria), Aggregation.group(HospitalConstants.WORK_DATE));
        AggregationResults<BookingScheduleRuleVo> aggregateResults = mongoTemplate.aggregate(totalAgg, Schedule.class, BookingScheduleRuleVo.class);
        //条数
        int size = aggregateResults.getMappedResults().size();
        //日期转换星期
        bookingScheduleRuleVos.forEach(bookingScheduleRuleVo -> {
            Date workDate = bookingScheduleRuleVo.getWorkDate();
            String week = DateUtils.dateToWeek(workDate);
            bookingScheduleRuleVo.setDayOfWeek(week);
        });
        //封装最终返回map
        HashMap<String, Object> resultMap = new HashMap<>();
        resultMap.put(HospitalConstants.BOOKING_SCHEDULE_RULE_LIST,bookingScheduleRuleVos);
        resultMap.put(HospitalConstants.TOTAL,size);
        //医院名字
        HashMap<String, String> baseMap = new HashMap<>();
        baseMap.put(HospitalConstants.HOSNAME,hospitalService.getHospitalByHoscode(hoscode).getHosname());
        resultMap.put(HospitalConstants.BASE_MAP,baseMap);
        return resultMap;
    }


    @Override
    public List<Schedule> getScheduleDetails(String hoscode, String depcode, String workDate) {
        //转date
        Date date = DateUtils.parseDateToYMD(workDate);
        //mongoRepository查询的时候，查询时间得不能用String类型
        List<Schedule> schedules = scheduleRepository.getScheduleByHoscodeAndDepcodeAndWorkDate(hoscode, depcode, date);
        //设置数据，医院名称，科室名称，日期转星期
        schedules.forEach(this::packSchedule);
        return schedules;
    }


    //经过良久排查还是不行，前端渲染出来的始终是无号
    @Override
    @CustomSuppressWarnings
    public Map<String, Object> getBookingScheduleRulePage(Integer pageNum, Integer pageSize, String hoscode, String depcode) {
        //最终结果
        HashMap<String, Object> result = new HashMap<>();
        //因为预约规则在这里面
        Hospital hospital = hospitalService.getHospitalByHoscode(hoscode);
        if (StringUtils.isNotNull(hospital)){
            BookingRule bookingRule = hospital.getBookingRule();
            //--? 推断: 大概率是这个datePage有问题
            IPage<Date> datePage = this.getDatePage(pageNum, pageSize, bookingRule);
            //当前可预约日期
            List<Date> dateList = datePage.getRecords();;//3.1 - 3.2 - 3.3 - 3.4 得到的日期,就是当前的系统日期
            //先转String查完再转
//            List<String> dates = DateUtils.dateListToStringList(dateList);
            Date dateStart = null;
            Date dateEnd = null;
            for (int i = 0; i < dateList.size(); i++) {
                dateStart = dateList.get(0);
                dateEnd = dateList.get(dateList.size()-1);
            }
            //获取可预约日期里科室的剩余数据 ---   加上日期就查不到了
            Criteria criteria = Criteria.where(HospitalConstants.HOSCODE).is(hoscode).and(HospitalConstants.DEPCODE).is(depcode).and(HospitalConstants.WORK_DATE).gte(dateStart).lte(dateEnd);
            /*
            总结：
            经过两天断点排查，多次试错，最终才发现是聚合查询语句有问题，以下是聚合查询最终执行的代码，
            发现$in查询并不能查询出结果(许久尝试后，推断可能是版本的原因或是日期的问题)
            mongodb的操作还不是很熟练，最终用一个范围查询来代替了in，大致就是:
            因为要查的是一个数组日期，主要是它们是从小到大相连的日期，然后查询它的范围就行了，仔细想想这样会有更多问题，暂时没想到更好的办法，先这样写着
            db.schedule.aggregate([
            { "$match" :
                { "hoscode" : "100", "depcode" : "200040878", "workDate" :
                    { "$in" : ["2021-01-17 00:00:00", "2021-01-18 00:00:00", "2021-01-19 00:00:00", "2021-01-20 00:00:00",
                                "2021-01-17 00:00:00", "2021-01-17 00:00:00", "2021-01-17 00:00:00", "2021-01-17 00:00:00",
                                "2021-01-17 00:00:00", "2021-01-17 00:00:00"]
                    }
                }
            },
            { "$group" :
                { "_id" : "$workDate", "workDate" : { "$first" : "$workDate"},
                "docCount" : { "$sum" : 1},
                "availableNumber" : { "$sum" : "$availableNumber"},
                "reservedNumber" : { "$sum" : "$reservedNumber"}
                }
            }
            ])
             */
            Aggregation aggregation = Aggregation.newAggregation(
                    //---
                    /*
                     * project:列出所有本次查询的字段，包括查询条件的字段和需要搜索的字段；
                     * match:搜索条件criteria
                     * unwind：某一个字段是集合，将该字段分解成数组
                     * group：分组的字段，以及聚合相关查询
                     *      sum：求和(同sql查询)
                     *      count：数量(同sql查询)
                     *      as:别名(同sql查询)
                     *      addToSet：将符合的字段值添加到一个集合或数组中
                     * sort：排序
                     * skip&limit：分页查询
                     */
                    Aggregation.match(criteria),
                    Aggregation.group(HospitalConstants.WORK_DATE).first(HospitalConstants.WORK_DATE).as(HospitalConstants.WORK_DATE)
                            .count().as(HospitalConstants.DOC_COUNT)
                            .sum(HospitalConstants.AVAILABLE_NUMBER).as(HospitalConstants.AVAILABLE_NUMBER)
                            .sum(HospitalConstants.RESERVED_NUMBER).as(HospitalConstants.RESERVED_NUMBER)
            );
            System.out.println("aggregation = " + aggregation);
            AggregationResults<BookingScheduleRuleVo> aggregateResult = mongoTemplate.aggregate(aggregation, Schedule.class, BookingScheduleRuleVo.class);
            List<BookingScheduleRuleVo> scheduleRuleVos = aggregateResult.getMappedResults();
            //放入map集合
            Map<Date, BookingScheduleRuleVo> scheduleVoMap = new HashMap<>();
            if (!CollectionUtils.isEmpty(scheduleRuleVos)){
                //分组吧 ---
                scheduleVoMap = scheduleRuleVos.stream().collect(Collectors.toMap(BookingScheduleRuleVo::getWorkDate,BookingScheduleRuleVo->BookingScheduleRuleVo));
            }
            //获取可预约排班规则 ---
            ArrayList<BookingScheduleRuleVo> bookingScheduleRuleVoList = new ArrayList<>();
            for (int i = 0, len = dateList.size(); i < len; i++) {
                Date date = dateList.get(i);
                BookingScheduleRuleVo bookingScheduleRuleVo = scheduleVoMap.get(date);
                //聚合查询到空的就给它赋值，含义是没有医生
                if (StringUtils.isNull(bookingScheduleRuleVo)){
                    bookingScheduleRuleVo = new BookingScheduleRuleVo();
                    bookingScheduleRuleVo.setDocCount(HospitalConstants.DOC_COUNT_VALUE);
                    bookingScheduleRuleVo.setAvailableNumber(HospitalConstants.AVAILABLE_NUMBER_VALUE);
                }
                bookingScheduleRuleVo.setWorkDate(date);
                bookingScheduleRuleVo.setWorkDateMd(date);
                //计算当前预约日期对应的星期
                String dateToWeek = DateUtils.dateToWeek(date);
                bookingScheduleRuleVo.setDayOfWeek(dateToWeek);

                //最后一页记录为即将预约的  0；正常 1：即将放号 -1：当天已停止挂号
                if (i == len -1 && pageNum == datePage.getPages()){
                    bookingScheduleRuleVo.setStatus(HospitalConstants.BOOKING_SCHEDULE_RULE_VALUE_WILL);
                }else {
                    bookingScheduleRuleVo.setStatus(HospitalConstants.BOOKING_SCHEDULE_RULE_VALUE_NORMAL);
                }
                //当天过了预约时间
                if (i == 0 && pageNum == 1){
                    Date stopTime = DateUtils.patchTime(bookingRule.getStopTime());
                    if (!stopTime.before(new Date())){
                        bookingScheduleRuleVo.setStatus(HospitalConstants.BOOKING_SCHEDULE_RULE_VALUE_STOP);
                    }
                }
                bookingScheduleRuleVoList.add(bookingScheduleRuleVo);
            }
            //可预约日期数据
            result.put(HospitalConstants.BOOKING_SCHEDULE_RULE_LIST,bookingScheduleRuleVoList);
            result.put(HospitalConstants.TOTAL,datePage.getTotal());
            //其他基础数据
            HashMap<String, String> baseMap = new HashMap<>();
            baseMap.put(HospitalConstants.HOSNAME, hospitalService.getHospitalByHoscode(hoscode).getHosname());
            Department department = departmentService.getDepartmentByHoscodeAndDepcode(hoscode, depcode);
            if (StringUtils.isNotNull(department)){
                baseMap.put(HospitalConstants.BIGNAME,department.getBigname());
                baseMap.put(HospitalConstants.DEPNAME,department.getDepname());
                baseMap.put(HospitalConstants.WORK_DATE_STRING,new SimpleDateFormat(DateUtils.YYYY_MM_ZN).format(DateUtils.getNowDate()));
                //放号时间
                baseMap.put(HospitalConstants.RELEASE_TIME,bookingRule.getReleaseTime());
                //停号时间
                baseMap.put(HospitalConstants.STOP_TIME,bookingRule.getStopTime());
                result.put(HospitalConstants.BASE_MAP,baseMap);
                return result;
                //着实有点复杂了
            }
            throw new BusinessException(BusinessEnum.NOT_FOUND_VALUE);
//            return null;
        }
        throw new BusinessException(BusinessEnum.NOT_FOUND_VALUE);
//        return null;
    }


    @Override
    public Schedule getScheduleByScheduleId(String scheduleId) {
        Schedule schedule = scheduleRepository.getScheduleByHosScheduleId(scheduleId);
        if (StringUtils.isNotNull(schedule)){
            return this.packSchedule(schedule);
        }
        throw new BusinessException(BusinessEnum.PARAM_ERROR);
//        return null;
    }


    @Override
    public ScheduleOrderVo getScheduleOrderVoById(String scheduleId) {
        ScheduleOrderVo scheduleOrderVo = new ScheduleOrderVo();
        Schedule schedule = this.getScheduleByScheduleId(scheduleId);
        if (StringUtils.isNotNull(schedule)){
            //获取预约规则信息,从hospital中获取
            Hospital hospital = hospitalService.getHospitalByHoscode(schedule.getHoscode());
            if (StringUtils.isNotNull(hospital)){
                BookingRule bookingRule = hospital.getBookingRule();
                if (StringUtils.isNotNull(bookingRule)){
                    scheduleOrderVo.setHoscode(schedule.getHoscode());
                    scheduleOrderVo.setHosname(hospital.getHosname());
                    scheduleOrderVo.setDepcode(schedule.getDepcode());
                    scheduleOrderVo.setDepname(departmentService.getDepartmentByHoscodeAndDepcode(schedule.getHoscode(),schedule.getDepcode()).getDepname());
                    scheduleOrderVo.setHosScheduleId(schedule.getHosScheduleId());
                    scheduleOrderVo.setAvailableNumber(schedule.getAvailableNumber());
                    scheduleOrderVo.setTitle(schedule.getTitle());
                    scheduleOrderVo.setReserveDate(schedule.getWorkDate());
                    scheduleOrderVo.setReserveTime(schedule.getWorkTime());
                    scheduleOrderVo.setAmount(schedule.getAmount());

                    //退号截至天数，就诊前一天为-1，当天为0
                    Integer quitDay = bookingRule.getQuitDay();
                    Date quitTime = DateUtils.patchTime(DateUtils.addDays(schedule.getWorkDate(), quitDay), bookingRule.getQuitTime());
                    scheduleOrderVo.setQuitTime(quitTime);
                    //预约开始
                    Date startTime = DateUtils.patchTime(bookingRule.getReleaseTime());
                    scheduleOrderVo.setStartTime(startTime);
                    //预约截至
                    Date endTime = DateUtils.patchTime(DateUtils.addDays(DateUtils.getNowDate(), bookingRule.getCycle()), bookingRule.getStopTime());
                    scheduleOrderVo.setEndTime(endTime);
                    //当天停止挂号
                    Date stopTime = DateUtils.patchTime(bookingRule.getStopTime());
                    scheduleOrderVo.setStopTime(stopTime);
                    return scheduleOrderVo;
                }
                throw new BusinessException(BusinessEnum.PARAM_ERROR);
//                return null;
            }
            throw new BusinessException(BusinessEnum.PARAM_ERROR);
//            return null;
        }
        throw new BusinessException(BusinessEnum.PARAM_ERROR);
//        return null;
    }


    @Override
    public Integer updateSchedule(Schedule schedule) {
        schedule.setUpdateTime(DateUtils.getNowDate());
        scheduleRepository.save(schedule);
        return 1;
    }


    /**
     * 设置排班详情里的 医院名称，科室名称，日期对应星期
     * @param schedule
     * @return
     */
    private Schedule packSchedule(Schedule schedule){
        schedule.getParam().put(HospitalConstants.HOSNAME,hospitalService.getHospitalByHoscode(schedule.getHoscode()).getHosname());
        schedule.getParam().put(HospitalConstants.DEPNAME,departmentService.getDepartmentByHoscodeAndDepcode(schedule.getHoscode(),schedule.getDepcode()).getDepname());
        schedule.getParam().put(HospitalConstants.DAY_OF_WEEK,DateUtils.dateToWeek(schedule.getWorkDate()));
        return schedule;
    }


    /**
     * 根据预约规则获取可预约数据分页
     * @param pageNum
     * @param pageSize
     * @param bookingRule
     */
    //测试此方法没有问题
    public IPage<Date> getDatePage(Integer pageNum, Integer pageSize, BookingRule bookingRule) {
        Date date = DateUtils.patchTime(bookingRule.getReleaseTime());
        //获取预约周期
        Integer cycle = bookingRule.getCycle();
        //感觉逻辑反着的 参数时间在现在时间之前，感觉返回false有点逆反常态逻辑，取反了
        if (!date.before(new Date())) {
            cycle += 1;
        }
        //可预约的所有日期，放在一个Date数组里
        ArrayList<Date> dates = new ArrayList<>();
        for (Integer integer = 0; integer < cycle; integer++) {
            Date curDate = DateUtils.addDays(new Date(), integer);
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DateUtils.YYYY_MM_DD);
            String formatResult = simpleDateFormat.format(curDate) +" "+"00:00:00";
            Date dateResult;
            try {
                dateResult = simpleDateFormat.parse(formatResult);
            } catch (ParseException e) {
                throw new DateException("预约周期出错");
            }
            dates.add(dateResult);
        }
        //预约周期大于七天的就分页显示
        ArrayList<Date> dateList = new ArrayList<>();
        int start = (pageNum - 1) * pageSize;
        int end = (pageNum - 1) * pageSize + pageSize;
        //小于等于七天的
        if (dates.size() < end){
            end = dates.size();
        }
        for (int i = start; i < end; i++) {
            dateList.add(dates.get(i));
        }
        //大于七天进行分页
        IPage<Date> datePage = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageNum, 7,dates.size());
        datePage.setRecords(dateList);
        return datePage;
    }



//    public IPage<Date> getDatePage(Integer pageNum, Integer pageSize, BookingRule bookingRule) {
//        DateTime releaseTime = DateUtils.getDateTime(new Date(),bookingRule.getReleaseTime());
//        //获取预约周期
//        Integer cycle = bookingRule.getCycle();
//        //感觉逻辑反着的 参数时间在现在时间之前，感觉返回false有点逆反常态逻辑，取反了
////        if (!date.before(new Date())) {
////            cycle += 1;
////        }
//        //当天放号时间过去预约周期+1
//        if (releaseTime.isBeforeNow()) {
//            cycle += 1;
//        }
//        //可预约的所有日期，放在一个Date数组里
//        ArrayList<Date> dates = new ArrayList<>();
//        for (int i = 0; i < cycle; i++) {
//            DateTime curDateTime = new DateTime().plusDays(i);
//            String dateString = curDateTime.toString("yyyy-MM-dd");
//            dates.add(new DateTime(dateString).toDate());
//        }
////        for (Integer integer = 0; integer < cycle; integer++) {
////            Date curDate = DateUtils.addDays(new Date(), integer);
////            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DateUtils.YYYY_MM_DD);
////            String formatResult = simpleDateFormat.format(curDate) +" "+"00:00:00";
////            Date dateResult;
////            try {
////                dateResult = simpleDateFormat.parse(formatResult);
////            } catch (ParseException e) {
////                throw new DateException("预约周期出错");
////            }
////            dates.add(dateResult);
////        }
//        //预约周期大于七天的就分页显示
//        ArrayList<Date> dateList = new ArrayList<>();
//        int start = (pageNum - 1) * pageSize;
//        int end = (pageNum - 1) * pageSize + pageSize;
//        //小于等于七天的
//        if (dates.size() < end){
//            end = dates.size();
//        }
//        for (int i = start; i < end; i++) {
//            dateList.add(dates.get(i));
//        }
//        //大于七天进行分页
//        IPage<Date> datePage = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageNum, 7,dates.size());
//        datePage.setRecords(dateList);
//        return datePage;
//    }



}
